<?php

@include "mysql/db.php";

class TFsBOXShout
	{
		public $date;
		public $id;
		public $ip;
		public $name;
		public $ranking;
		public $report;
		public $shout;
		public $site;
		public $user_id;

		function __construct($id, $ip, $name, $shout, $report, $date, $site)
			{
				$this->id = $id;
				$this->ip = $ip;
				$this->date = $date;
				$this->name = $name;
				$this->ranking = 3;
				$this->report = $report;
				$this->shout = $shout;
				$this->site = $site;
			}

		function __toString()
			{
				return $this->shout;
			}

		function prepareShout()
			{
				return base64_encode($this->ip) ."||".
					   base64_encode($this->name) ."||".
					   base64_encode($this->shout) ."||".
					   $this->report ."||".
					   base64_encode($this->date) ."||".
					   base64_encode($this->site);
			}
	}

class TFsBOXWordFilter
	{
		public $word;
		public $filter;

		function __construct($word, $filter)
			{
				$this->word = $word;
				$this->filter = $filter;
			}

		function __toString()
			{
				return $this->word."=".$this->filter;
			}
	}

class TFsBOXDatabase extends TFsBOXDebugger
	{
		// Database modes
		const FLATFILE = 0;
		const MYSQL = 1;

		static protected $bbcodes = array("^_^" => "\^_\^",
										  "agreed" => ":agreed:",
										  "blink" => ":blink:|:blinks:",
										  "blush" => ":blush:|:blushs:",
										  "bored" => ":bored:",
										  "bounce" => ":bounce:",
										  "bunny" => ":bunny:|:rabbit:",
										  "cat" => ":cat:|:kitty:",
										  "check" => ":check:|:checkmark:",
										  "cheer" => ":cheer:|:yay:",
										  "confused" => ":\?\?\?:|:confused:",
										  "cool" => "B\)|:cool:",
										  "cooltongue" => ":BP:",
										  "cow" => ":cow:|:beef:",
										  "cowboy" => "C\|:\)|:cowboy:",
										  "dance" => ":dance:|:dancing:",
										  "dead" => "x_x|:dead:",
										  "dog" => ":dog:|:woof:",
										  "drool" => ":drool:|:drools:",
										  "drumroll" => ":drumroll:|:drums:",
										  "drunk" => ":drunk:|:drunkard:",
										  "evilgrin" => ">:D|>:\-D",
										  "file" => ":file:|:document:",
										  "fish" => ":fish:|><>",
										  "flatface" => ":\||:\-\|",
										  "frown" => ":\(|:\-\(",
										  "gasp" => ":gasp:|:@|:\-@|D:|D\-:",
										  "giggle" => ":giggle:|:giggles:",
										  "gossip" => ":gossip:",
										  "h5" => ":high5:|:highfive:|:\^5:",
										  "happy" => ":happy:|:D|:\-D",
										  "hug" => ":hug:|:huggles:|:hugs:",
										  "hugpig" => ":hugpig:|:pighug:|:hugspig:|:pighuggles:",
										  "huh" => ":huh:|:err:",
										  "joy" => ":joy:|:ohjoy:",
										  "lmao" => "lmao|lmso|rofl",
										  "lol" => "lol",
										  "mad" => "grr|>:\(|>:\-\(|:mad:|:angry:",
										  "moneystruck" => "$\)|$\-\)",
										  "monkey" => ":monkey:",
										  "no" => ":no:",
										  "nothingtosay" => ":X|:\-X",
										  "otter" => ":otter:",
										  "pet" => ":pet:",
										  "pig" => ":pig:|:oink:|:pork:",
										  "pray" => ":pray:|:praying:",
										  "roflmao" => "roflmao|roflmso",
										  "rolleyes" => ":rolleyes:|:rollseyes:",
										  "s" => ":S|:\-S",
										  "sigh" => "\-_\-|v.v|v_v",
										  "sleep" => ":zzz:|:sleep:|:snooze:|:snore:",
										  "smile" => ":\)|:\-\)|:smile:",
										  "spam" => ":spam:",
										  "surprised" => ":O|:\-O|:wow:|:whoa:",
										  "t_t" => "T_T",
										  "tear" => ":'\(|:'\-\(",
										  "think" => ":think:|:thinking:",
										  "tongue" => ":P|:\-P",
										  "unsure" => ":\\\|:\-\\\\",
										  "wanthug" => ":hugme:|:wanthug:",
										  "wink" => ";\)|;\-\)|:wink:",
										  "winktongue" => ";P|;\-P",
										  "woot" => ":woot:",
										  "worship" => ":worship:",
										  "x" => ":x:",
										  "xfrown" => "x\(|x\-\(",
										  "xgrin" => "xD|x\-D",
										  "xsmile" => "x\)|x\-\)",
										  "xtongue" => "xP|x\-P",
										  "yawn" => ":yawn:|:tired:",
										  "yes" => ":yes:|:yup:|:yep:",
										  "yummy" => ":9|:\-9|:yummy:"
										  );
		static protected $database_connection;
		static protected $database_mode = 0;
		static protected $ip_cache;
		static protected $settings_cache;
		static protected $shouts_cache;
		static protected $smiley_cache;
		static protected $url_cache;
		static protected $word_filters_cache;

		function __construct()
			{
				$db_type = strtolower(TFsBOX::$Config->value("db_type", "flatfile"));
				if ($db_type == "flatfile")
					$this->setDatabaseMode(self::FLATFILE);
				else if ($db_type == "mysql")
				{
					$this->setDatabaseMode(self::MYSQL);

					// Revert to flatfile upon failure to connect to database.
					if (!$this->connectToMysql(TFsBOX_DB_HOST, TFsBOX_DB_USER, TFsBOX_DB_PASS))
					{
						$this->setDatabaseMode(self::FLATFILE);
						TFsBOXSession::storeNotice("System has reverted to flatfile mode.");
					}

					else
					{
						$this->refreshSettings();
					}
				}

				// Shouts cache is really only necessary for flatfile mode
				if (!self::$database_mode)
					$this->refreshShouts();

				$this->refreshIps();
				$this->refreshSmilies();
				$this->refreshUrls();
				$this->refreshWordFilters();
			}

		function addShout()
			{
				if ($this->allowedToShout())
				{
					// Flatfile
					if (!self::$database_mode)
					{
						if (is_writable(TFsBOX_SHOUTS_FILE))
						{
							$r = (bool) file_put_contents(TFsBOX_SHOUTS_FILE,
														  $this->prepareShout()."\n",
														  FILE_APPEND | FILE_TEXT);
							$this->refreshShouts();
							return $r;
						}

						else
						{
							TFsBOXSession::storeError("The file <tt>". basename(TFsBOX_SHOUTS_FILE) ."</tt> is not writable.");
							return false;
						}
					}

					else if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$shout = mysql_real_escape_string(TFsBOX::$Session->currentShout());
							$sql = "INSERT INTO ". TFsBOX_DB_PREFIX ."shouts "
								  ."(`id`, `user_id`, `ip`, `name`, `shout`, `site`, `date`, `report`) "
								  ."values ";
							if (TFsBOX::$Session->loggedIn())
								$sql .= "(NULL, '". TFsBOX::$Session->userId() ."', '". ip2long(TFsBOX::$Session->ip()) ."', NULL, '$shout', NULL, NOW(), '0')";
							else
								$sql .= "(NULL, '0', '". ip2long(TFsBOX::$Session->ip()) ."', '". TFsBOX::$Session->username() ."', '$shout', '". TFsBOX::$Session->userSite() ."', NOW(), '0')";

							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("There was a problem adding the shout.");

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function addUser($username, $password, $email, $site, $v_code = "NULL")
			{
				if (!preg_match(TFsBOX::NameRegex, $username))
				{
					TFsBOXSession::storeError("Username must be made up of only letters, numbers, and underscores&mdash;anything else is not allowed, this includes spaces.");
					return false;
				}

				if ($this->userExists($username))
				{
					TFsBOXSession::storeError("The desired username is already taken. You'll have to pick another one.");
					return false;
				}

				// Password doesn't need verification.
				$password = TFsBOX::passwordHash($username, $password);

				if (!preg_match(TFsBOX::EmailRegex, $email))
				{
					TFsBOXSession::storeError("Invalid e-mail address provided.");
					return false;
				}

				if (!preg_match(TFsBOX::SiteRegex, $site)
				 && !empty($site))
				{
					TFsBOXSession::storeError("Invalid URL provided for site.");
					return false;
				}

				if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `id` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."WHERE `email` = '$email' LIMIT 1";
						if ($result = mysql_query($sql))
						{
							if (mysql_num_rows($result))
							{
								TFsBOXSession::storeError("The e-mail address provided is already in use.");
								return false;
							}
						}

						$sql = "INSERT INTO ". TFsBOX_DB_PREFIX ."users "
							  ."(`id`, `username`, `password`, `email`, `site`, `verified`, `ranking`, `login_attempts`, `last_login_attempt`, `login_ips`, `banned`, `auth_token`, `form_position`, `reset_password`, `subscriptions`) "
							  ."values "
							  ."(NULL, '$username', '$password', '$email', '$site', NULL, '". TFsBOXSession::REGULAR ."', '0', NULL, '". $_SERVER['REMOTE_ADDR'] ."\n', NULL, $v_code, '". TFsBOX::$Config->value("form_position", "bottom") ."', NULL, '')";
						if (!$result = mysql_query($sql))
							TFsBOXSession::storeError("There was a problem registering the new user.");

						return (bool) $result;
					}
				}

				return false;
			}

		function addWordFilter($word, $filter)
			{
				// Only admin can add a word filter
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					if (!preg_match("#^\W+$#", $word)
					 && preg_match("#^\[?[\w\*]+\]?$#", $filter))
					{
						return (bool) file_put_contents(TFsBOX_WORDFILTERS_FILE,
														$word."=".$filter."\n",
														FILE_APPEND | FILE_TEXT);
					}
				}

				return false;
			}

		function adjustSettings($shoutbox_path,
								$verification_method,
								$max_login_attempts,
								$allow_guests)
			{
				// Only admin can adjust settings
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$shoutbox_path = mysql_real_escape_string($shoutbox_path);
							$verification_method = ($verification_method == "cpanel") ? "cpanel" : "email";
							$max_login_attempts = (int) $max_login_attempts;
							$allow_guests = ($allow_guests == "yes") ? 1 : 0;

							if (!$max_login_attempts || $max_login_attempts > 9)
							{
								TFsBOXSession::storeNotice("Maximum login attempts must be greater than 0 but less than 10. Defaulting to 5.");
								$max_login_attempts = 5;
							}

							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."settings SET "
								  ."`shoutbox_path` = '$shoutbox_path', "
								  ."`verification_method` = '$verification_method', "
								  ."`max_login_attempts` = '$max_login_attempts', "
								  ."`allow_guests` = '$allow_guests' "
								  ."WHERE `id` = '1'";

							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("Unable to adjust settings: ". mysql_errno() .": ". mysql_error());

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function allowedToShout($store_errors = true, $consider_time = true)
			{
				if ($this->isIpBanned($_SERVER['REMOTE_ADDR']))
				{
					if ($store_errors)
						TFsBOXSession::storeError("You are not allowed to shout.");
					return false;
				}

				if (!TFsBOX::$Session->loggedIn()
				 && !$this->guestsAllowed())
				{
					if ($store_errors)
						TFsBOXSession::storeError("Guests aren't allowed to shout.");
					return false;
				}

				$last_shout = $this->lastShoutByIp();

				// Hasn't shouted before
				if (!$last_shout)
					return true;

				// Anti-spam timeout
				if (strtotime("+". (int) TFsBOX::$Config->value("antispam_timeout", 5) ." seconds", $last_shout->date)
				  > time() && $consider_time)
				{
					if ($store_errors)
						TFsBOXSession::storeNotice("You must wait ". (int) TFsBOX::$Config->value("antispam_timeout", 5) ." seconds before you are able to shout again.");
					return false;
				}

				return true;
			}

		function approveUser($user_id)
			{
				$user_id = (int) $user_id;

				// Make sure the user ID is greater than 0.
				if (!$user_id)
					return false;

				if (self::$database_connection)
				{
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "UPDATE ". TFsBOX_DB_PREFIX ."users SET "
							  ."`verified` = '1', `auth_token` = NULL "
							  ."WHERE `id` = '$user_id'";
						if (!$result = mysql_query($sql))
							TFsBOXSession::storeError("Could not validate user: " . mysql_error());

						return (bool) $result;
					}
				}

				return false;
			}

		function banIp($ip)
			{
				// Only privileged members can ban IPs
				if (TFsBOX::$Session->userRanking()
				 != TFsBOXSession::REGULAR)
				{
					if (preg_match(TFsBOX::IpRegex, $ip))
					{
						// Check if IP is not already banned
						if (!$this->isIpBanned($ip))
						{
							$ip = ip2long($ip);
							return (bool) file_put_contents(TFsBOX_IP_FILE,
															$ip."\n",
															FILE_APPEND | FILE_TEXT);
						}

						else TFsBOXSession::storeNotice("The IP <tt>$ip</tt> is already banned.");
					}

					else TFsBOXSession::storeError("Invalid IP address provided.");
				}

				return false;
			}

		function bannedIps()
			{
				if (empty(self::$ip_cache))
					$this->refreshIps();

				return self::$ip_cache;
			}

		function bannedUrls()
			{
				if (empty(self::$url_cache))
					$this->refreshUrls();

				return self::$url_cache;
			}

		function banUrl($url)
			{
				// Only privileged members can ban URLs
				if (TFsBOX::$Session->userRanking()
				 != TFsBOXSession::REGULAR)
				{
					if (preg_match(TFsBOX::SiteRegex, $url))
					{
						// Check if URL is not already banned
						if (!$this->isUrlBanned($url))
						{
							return (bool) file_put_contents(TFsBOX_URL_FILE,
															$url."\n",
															FILE_APPEND | FILE_TEXT);
						}

						else TFsBOXSession::storeNotice("URL <tt>$url</tt> is already banned.");
					}

					else TFsBOXSession::storeError("Invalid URL provided.");
				}

				return false;
			}

		function clearReport($id)
			{
				$id = (int) $id;

				// Only privileged members can clear reports
				if (TFsBOX::$Session->userRanking()
				 != TFsBOXSession::REGULAR)
				{
					// Flatfile
					if (!self::$database_mode)
					{
						if ($f = fopen(TFsBOX_SHOUTS_FILE, 'w'))
						{
							$content = "";
							stream_set_write_buffer($f, 0);
							foreach (self::$shouts_cache as $shout)
							{
								if ($shout->id == $id)
									$shout->report = 0;

								$content .= $shout->prepareShout()."\n";
							}
							fwrite($f, $content);
							fclose($f);
							return true;
						}
					}

					else if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."shouts SET "
								  ."`report` = '0' WHERE `id` = '$id'";
							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("There was a problem clearing the report.");

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function clearReports()
			{
				// Only admin can clear ALL reports
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					// Flatfile
					if (!self::$database_mode)
					{
						if ($f = fopen(TFsBOX_SHOUTS_FILE, 'w'))
						{
							$content = "";
							stream_set_write_buffer($f, 0);
							foreach (self::$shouts_cache as $shout)
							{
								$shout->report = 0;
								$content .= $shout->prepareShout()."\n";
							}
							fwrite($f, $content);
							fclose($f);
							return true;
						}
					}

					else if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."shouts SET "
								  ."`report` = '0' WHERE `report` = '1'";
							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("There was a problem clearing all the shouts.");

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function clearShouts()
			{
				// Only admin can clear ALL shouts
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					// Flatfile
					if (!self::$database_mode)
						return (file_put_contents(TFsBOX_SHOUTS_FILE, "") !== false);

					// MySQL
					else if (self::$database_connection)
					{
						if (self::$database_mode
						 == self::MYSQL)
							return (bool) mysql_query("TRUNCATE TABLE ". TFsBOX_DB_PREFIX ."shouts");
					}
				}

				return false;
			}

		function connectToMysql($db_host, $db_user, $db_pass)
			{
				self::$database_connection = @mysql_connect($db_host, $db_user, $db_pass);
				return @mysql_select_db(TFsBOX_DB_NAME);
			}

		function createTable($sql, $table_name, $db_name)
			{
				if (self::$database_connection)
				{
					if (self::$database_mode
					 == self::MYSQL)
					{
						mysql_select_db($db_name);
						$sql = "CREATE TABLE IF NOT EXISTS `$table_name` ("
							  .$sql
							  .") ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci;";

						if (!$result = mysql_query($sql))
							TFsBOXSession::storeError("Unable to create table: ". mysql_errno() .": ". mysql_error());

						return (bool) $result;
					}
				}

				return false;
			}

		function databaseConnection()
			{
				return self::$database_connection;
			}

		function databaseMode()
			{
				return self::$database_mode;
			}

		function deleteShout($id)
			{
				$id = (int) $id;

				// Only privileged members can delete a shout
				if (TFsBOX::$Session->userRanking()
				 != TFsBOXSession::REGULAR)
				{
					// Flatfile
					if (!self::$database_mode)
					{
						if ($f = fopen(TFsBOX_SHOUTS_FILE, 'w'))
						{
							$content = "";
							stream_set_write_buffer($f, 0);
							foreach (self::$shouts_cache as $shout)
							{
								if ($shout->id != $id)
									$content .= $shout->prepareShout()."\n";
							}
							fwrite($f, $content);
							fclose($f);
							return true;
						}
					}

					else if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$sql = "DELETE FROM ". TFsBOX_DB_PREFIX ."shouts WHERE `id` = '$id'";
							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("There was a problem deleting the shout.");

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function dropTable($table_name, $db_name)
			{
				if (self::$database_connection)
				{
					if (self::$database_mode
					 == self::MYSQL)
					{
						mysql_select_db($db_name);
						if (!$result = mysql_query("DROP TABLE IF EXISTS `$table_name`"))
							TFsBOXSession::storeError("Failed to drop table <tt>$table_name</tt>: ". mysql_errno() .": ". mysql_error());

						return (bool) $result;
					}
				}

				return false;
			}

		function editShout($id)
			{
				$id = (int) $id;

				// To avoid abuse, only privileged members can edit a shout
				// Timeouts will not be implemented (as with previous versions)
				if (TFsBOX::$Session->userRanking()
				 != TFsBOXSession::REGULAR)
				{
					// Flatfile
					if (!self::$database_mode)
					{
						if ($f = fopen(TFsBOX_SHOUTS_FILE, 'w'))
						{
							$content = "";
							stream_set_write_buffer($f, 0);
							foreach (self::$shouts_cache as $shout)
							{
								if ($shout->id == $id)
									$shout->shout = TFsBOX::$Session->currentShout();

								$content .= $shout->prepareShout()."\n";
							}
							fwrite($f, $content);
							fclose($f);
							return true;
						}
					}

					else if (self::$database_connection)
					{
						// MySQL
						if (self::$database_mode
						 == self::MYSQL)
						{
							$shout = mysql_real_escape_string(TFsBOX::$Session->currentShout());
							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."shouts SET "
								  ."`shout` = '$shout' "
								  ."WHERE `id` = '$id'";
							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("There was a problem when editing the shout.");

							return (bool) $result;
						}
					}
				}

				return false;
			}

		function emailExists($email)
		{
		}

		function guestsAllowed()
			{
				if (!self::$database_mode)
					return true;
				else if (isset(self::$settings_cache['allow_guests']))
					return (bool) self::$settings_cache['allow_guests'];

				return false;
			}

		function isIpBanned($ip)
			{
				return in_array($ip, self::$ip_cache);
			}

		function isUrlBanned($url)
			{
				return in_array($url, self::$url_cache);
			}

		protected function lastShoutByIp()
			{
				// Flatfile
				if (!self::$database_mode)
				{
					$shouts = array_reverse($this->shouts());
					foreach ($shouts as $shout)
					{
						if ($shout->ip == TFsBOX::$Session->ip())
							return $shout;
					}
				}

				else if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT * FROM ". TFsBOX_DB_PREFIX ."shouts "
							  ."WHERE `ip` = '". ip2long(TFsBOX::$Session->ip()) ."' LIMIT 1 ORDER BY DESC";
						if ($result = mysql_query($sql))
						{
							return mysql_fetch_object($result);
						}
					}
				}

				return false;
			}

		function latestUsers($limit = 10)
			{
				$limit = (int) $limit;

				if (self::$database_connection)
				{
					$return = array();

					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `id`, `username`, `ranking` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."ORDER BY `id` DESC LIMIT $limit";
						if ($result = mysql_query($sql))
						{
							while ($arr = mysql_fetch_assoc($result))
								$return[] = $arr;

							return $return;
						}
					}
				}

				return false;
			}

		function moderators()
			{
				if (self::$database_connection)
				{
					$return = array();

					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `id`, `username` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."WHERE `ranking` = '". TFsBOXSession::MODERATOR ."'";
						if ($result = mysql_query($sql))
						{
							while ($arr = mysql_fetch_assoc($result))
								$return[] = $arr;
						}
					}

					return $return;
				}

				return false;
			}

		function numOfShouts()
			{
				if (!self::$database_mode)
					return count($this->shouts());

				else if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						return (int) ($result = mysql_query("SELECT COUNT(*) FROM ". TFsBOX_DB_PREFIX ."shouts"))
							  ? mysql_result($result, 0) : 0;
					}
				}

				return 0;
			}

		function prepareShout()
			{
				$shout = array
				(
					base64_encode(TFsBOX::$Session->ip()),
					base64_encode(TFsBOX::$Session->username()),
					base64_encode(TFsBOX::$Session->currentShout()),
					0,
					base64_encode(time()),
					base64_encode(TFsBOX::$Session->userSite())
				);
				return implode("||", $shout);
			}

		function promoteUser($user_id, $ranking = NULL)
			{
				// Only admin can promote users
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					$user_id = (int) $user_id;
					$ranking = (int) ($ranking === NULL) ? TFsBOXSession::MODERATOR : $ranking;

					// Make sure the user ID is greater than 0.
					if (!$user_id)
						return false;

					//	Likewise the ranking.
					if (!$ranking)
						$ranking = TFsBOXSession::MODERATOR;

					if (self::$database_connection)
					{
						if (self::$database_mode
						 == self::MYSQL)
						{
							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."users SET "
								  ."`ranking` = '$ranking' "
								  ."WHERE `id` = '$user_id'";
							if (!$result = mysql_query($sql))
								TFsBOXSession::storeError("Could not promote user: " . mysql_error());

							return $result;
						}
					}
				}

				return false;
			}

		protected function refreshIps()
			{
				self::$ip_cache = explode("\n", file_get_contents(TFsBOX_IP_FILE));
				if (is_array(self::$ip_cache))
					array_pop(self::$ip_cache);

				self::$ip_cache = array_map("long2ip", self::$ip_cache);
			}

		protected function refreshSettings()
			{
				if (self::$database_connection)
				{
					if (self::$database_mode
					 == self::MYSQL)
					{
						self::$settings_cache = array();

						$sql = "SELECT `shoutbox_path`, `verification_method`, `max_login_attempts`, `allow_guests` "
							  ."FROM ". TFsBOX_DB_PREFIX ."settings WHERE `id` = '1'";
						if ($result = mysql_query($sql))
							self::$settings_cache = mysql_fetch_array($result);
					}
				}
			}

		protected function refreshShouts()
			{
				self::$shouts_cache = null;
				$this->shouts();
			}

		protected function refreshSmilies()
			{
				self::$smiley_cache = array();

				$dir = dirname(__FILE__)."/";
				$dir .= TFsBOX::$Config->value("smiley_pack", "/images/smileys/trillian");

				if (file_exists($dir))
				{
					$smileys = scandir($dir);
					foreach ($smileys as $file)
					{
						if ($file == "."
						 || $file == ".."
						 || $file == ".svn")
							continue;

						$path_info = pathinfo($file);
						$filename = preg_replace("/\.\w+$/", "", $path_info['basename']);
						self::$smiley_cache[$filename] = $path_info['basename'];
					}
				}
			}

		protected function refreshUrls()
			{
				self::$url_cache = explode("\n", file_get_contents(TFsBOX_URL_FILE));
				if (is_array(self::$url_cache))
					array_pop(self::$url_cache);
			}

		protected function refreshWordFilters()
			{
				self::$word_filters_cache = array();
				$tmp = explode("\n", file_get_contents(TFsBOX_WORDFILTERS_FILE));
				array_pop($tmp);

				foreach ($tmp as $word)
				{
					$explode = explode("=", $word);
					self::$word_filters_cache[]
							= new TFsBOXWordFilter($explode[0],
												   $explode[1]);
				}
			}

		function removeIps(array $ips)
			{
				// Only admin can unban an IP address
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					if (empty(self::$ip_cache))
						$this->refreshIps();

					if ($f = fopen(TFsBOX_IP_FILE, 'w'))
					{
						$content = "";
						stream_set_write_buffer($f, 0);
						foreach (self::$ip_cache as $ip)
						{
							if (in_array($ip, $ips))
								continue;

							$content .= $ip."\n";
						}
						fwrite($f, $content);
						fclose($f);
						return true;
					}
				}

				return false;
			}

		function removeUrls(array $urls)
			{
				// Only admin can unban a URL
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{

					if (empty(self::$url_cache))
						$this->refreshUrls();

					if ($f = fopen(TFsBOX_URL_FILE, 'w'))
					{
						$content = "";
						stream_set_write_buffer($f, 0);
						foreach (self::$url_cache as $url)
						{
							if (in_array($url, $urls))
								continue;

							$content .= $url."\n";
						}
						fwrite($f, $content);
						fclose($f);
						return true;
					}
				}

				return false;
			}

		function removeWordFilters(array $words)
			{
				// Only admin can remove a word filter
				if (TFsBOX::$Session->userRanking()
				 == TFsBOXSession::ADMIN)
				{
					if (empty(self::$word_filters_cache))
						$this->refreshWordFilters();

					if ($f = fopen(TFsBOX_WORDFILTERS_FILE, 'w'))
					{
						$content = "";
						stream_set_write_buffer($f, 0);
						foreach (self::$word_filters_cache as $filter)
						{
							if (in_array($filter->word, $words))
								continue;

							$content .= (string) $filter."\n";
						}
						fwrite($f, $content);
						fclose($f);
						return true;
					}
				}

				return false;
			}

		function report($id)
			{
				$id = (int) $id;

				// Flatfile
				if (!self::$database_mode)
				{
					if ($f = fopen(TFsBOX_SHOUTS_FILE, 'w'))
					{
						$content = "";
						stream_set_write_buffer($f, 0);
						foreach (self::$shouts_cache as $shout)
						{
							if ($shout->id == $id
							 && strtolower($shout->name) != strtolower(TFsBOX::USERNAME))
								$shout->report = 1;

							$content .= $shout->prepareShout()."\n";
						}
						fwrite($f, $content);
						fclose($f);
						return true;
					}
				}

				// MySQL
				else if (self::$database_mode
					  == self::MYSQL)
				{
				}

				return false;
			}

		function setDatabaseMode($mode)
			{
				self::$database_mode = (int) $mode;
			}

		function settings()
			{
				if (empty(self::$settings_cache))
					$this->refreshSettings();

				return self::$settings_cache;
			}

		function setUserSite($user_id, $site_url)
			{
				$user_id = (int) $user_id;

				// Make sure the user ID is greater than 0.
				if (!$user_id)
					return false;

				if (!preg_match(TFsBOX::SiteRegex, $site_url))
				{
					TFsBOXSession::storeError("Invalid URL provided for site.");
					return false;
				}

				if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."users SET "
								  ."`site` = '$site_url' "
								  ."WHERE `id` = '$user_id'";
							return (bool) mysql_query($sql);
					}
				}

				return false;
			}

		function shout($id)
			{
				$id = (int) $id;

				// Flatfile
				if (!self::$database_mode)
				{
					if (empty(self::$shouts_cache))
						$this->refreshShouts();

					foreach (self::$shouts_cache as $shout)
					{
						if ($shout->id == $id)
							return $shout;
					}
				}

				else if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						if ($result = mysql_query("SELECT `shout` FROM ". TFsBOX_DB_PREFIX ."shouts WHERE `id` = '$id' LIMIT 1"))
							return mysql_result($result, 0);
					}
				}

				return "";
			}

		function shouts()
			{
				// Flatfile
				if (!self::$database_mode)
				{
					if (!empty(self::$shouts_cache)
					 && is_array(self::$shouts_cache))
						return self::$shouts_cache;

					$mess = explode("\n", file_get_contents(TFsBOX_SHOUTS_FILE));
					if (is_array($mess))
						array_pop($mess);

					$shouts = array();

					foreach ($mess as $key => $value)
					{
						list($ip, $name, $shout, $report, $date, $site_url) = explode("||", $value);
						$shouts[] = new TFsBOXShout($key,
													base64_decode($ip),
													base64_decode($name),
													stripslashes(base64_decode($shout)),
													$report,
													base64_decode($date),
													base64_decode($site_url));
					}

					return self::$shouts_cache = $shouts;
				}

				else if (self::$database_connection)
				{
					$objs = array();

					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$offset = (TFsBOX::$Session->currentShoutPage()-1) * (int) TFsBOX::$Config->value("shouts_per_page", 10);
						$sql = "SELECT *, UNIX_TIMESTAMP(date) as `date` FROM ". TFsBOX_DB_PREFIX ."shouts ";

						if (defined("TFsBOX_SHOUTS_ONLY") && preg_match(TFsBOX::NameRegex, $_SERVER['QUERY_STRING']))
						{
							$user_id = $this->userId($_SERVER['QUERY_STRING']);
							if ($user_id)
								$sql .= "WHERE `user_id` = '$user_id' ";
						}

						$sql .= "ORDER BY `id` DESC "
							   ."LIMIT $offset,". (int) TFsBOX::$Config->value("shouts_per_page", 10);

						if ($result = mysql_query($sql))
						{
							while ($obj = mysql_fetch_object($result))
							{
								if ($obj->user_id)
								{
									$user_info = $this->userInfo($obj->user_id);
									if ($user_info)
									{
										$obj->name = $user_info->username;
										$obj->ranking = $user_info->ranking;
										$obj->site = $user_info->site;
									}
								}

								$obj->ip = long2ip($obj->ip);

								$i = count($objs);
								$objs[$i] = $obj;
							}
						}
					}

					return $objs;
				}

				return array();
			}

		function shoutsByIp($shout_ip)
			{
				// Flatfile
				if (!self::$database_mode)
				{
					if (empty(self::$shouts_cache))
						$this->refreshShouts();

					$shouts = array();

					foreach (self::$shouts_cache as $shout)
					{
						if ($shout->ip == $shout_ip)
							$shouts[] = $value;
					}

					return $shouts;
				}

				// MySQL
				else if (self::$database_mode
					  == self::MYSQL)
				{
				}
			}

		function shoutsByName($shout_name)
			{
				// Flatfile
				if (!self::$database_mode)
				{
					if (empty(self::$shouts_cache))
						$this->refreshShouts();

					$shouts = array();

					foreach (self::$shouts_cache as $shout)
					{
						if ($shout->name == $shout_name)
							$shouts[] = $value;
					}

					return $shouts;
				}

				// MySQL
				else if (self::$database_mode
					  == self::MYSQL)
				{
				}
			}

		function shoutsByUrl($shout_site)
			{
				// Flatfile
				if (!self::$database_mode)
				{
					if (empty(self::$shouts_cache))
						$this->refreshShouts();

					$shouts = array();

					foreach (self::$shouts_cache as $shout)
					{
						if ($shout->site == $shout_site)
							$shouts[] = $value;
					}

					return $shouts;
				}

				// MySQL
				else if (self::$database_mode
					  == self::MYSQL)
				{
				}
			}

		function smiley($name)
			{
				if (empty(self::$smiley_cache))
					$this->refreshSmilies();

				if (isset(self::$smiley_cache[$name]))
					return self::$smiley_cache[$name];

				return false;
			}

		function smileyBbcodes()
			{
				if (func_num_args())
					return self::$bbcodes[func_get_arg(0)];
				else
					return self::$bbcodes;
			}

		function smileyPacks()
			{
				static $packs = array();

				if (empty($packs))
				{
					$dir = dirname(__FILE__)."/images/smileys";

					foreach(scandir($dir) as $x)
					{
						if (is_dir($dir."/".$x)
						 && $x != "."
						 && $x != ".."
						 && $x != ".svn")
							$packs[] = $dir."/".$x;
					}
				}

				return $packs;
			}

		function smilies()
			{
				if (empty(self::$smiley_cache))
					$this->refreshSmilies();

				return self::$smiley_cache;
			}

		function subscribe($user_id, $user)
			{
				$user_id = (int) $user_id;

				// Make sure the user ID is greater than 0.
				if (!$user_id)
					return false;

				if (!preg_match(TFsBOX::NameRegex, $user))
				{
					TFsBOXSession::storeError("Invalid subscription.");
					return false;
				}

				if (self::$database_connection)
				{
					if (!$this->userExists($user))
					{
						TFsBOXSession::storeError("There is no user by that name to subscribe to.");
						return false;
					}

					if (in_array($user, TFsBOX::$Session->userSubscriptions()))
					{
						TFsBOXSession::storeError("You are already subscribed to <tt>$user</tt>.");
						return false;
					}

					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "UPDATE ". TFsBOX_DB_PREFIX ."users SET "
							  ."`subscriptions` = CONCAT(subscriptions, '$user\n') "
							  ."WHERE `id` = '$user_id'";
						return (bool) mysql_query($sql);
					}
				}

				return false;
			}

		function subscriptions($user_id)
			{
				$user_id = (int) $user_id;

				// Make sure the user ID is greater than 0.
				if (!$user_id)
					return false;

				if ($result = mysql_query("SELECT `subscriptions` FROM ". TFsBOX_DB_PREFIX ."users WHERE `id` = '$user_id' LIMIT 1"))
					return mysql_fetch_object($result);

				return false;
			}

		function unsubscribe($user_id, $subscription)
			{
				$user_id = (int) $user_id;

				// Make sure the user ID is greater than 0.
				if (!$user_id)
					return false;

				if (!preg_match(TFsBOX::NameRegex, $subscription))
				{
					TFsBOXSession::storeError("Invalid subscription.");
					return false;
				}

				if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `subscriptions` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."WHERE `id` = '$user_id' LIMIT 1";
						if ($result = mysql_query($sql))
						{
							$subs = mysql_result($result, 0);
							$subs = array_filter(explode("\n", $subs));
							$key = array_search($subscription, $subs);
							if ($key !== false)
								unset($subs[$key]);

							$sql = "UPDATE ". TFsBOX_DB_PREFIX ."users SET "
								  ."`subscriptions` = '". implode("\n", $subs) ."\n' "
								  ."WHERE `id` = '$user_id'";
							return (bool) mysql_query($sql);
						}
					}
				}

				return false;
			}

		function userExists($username)
			{
				if (!$username
				 || !preg_match(TFsBOX::NameRegex, $username))
					return null;

				// Flatfile
				if (!self::$database_mode)
				{
					if (strtolower(TFsBOX::USERNAME) == strtolower($username))
						return true;
				}

				else if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `id` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."WHERE `username` = '$username' LIMIT 1";
						if ($result = mysql_query($sql))
							return (bool) mysql_num_rows($result);
					}
				}

				return false;
			}

		function userId($username)
			{
				if (!$username
				 || !preg_match(TFsBOX::NameRegex, $username))
					return null;

				if (self::$database_connection)
				{
					// MySQL
					if (self::$database_mode
					 == self::MYSQL)
					{
						$sql = "SELECT `id` FROM ". TFsBOX_DB_PREFIX ."users "
							  ."WHERE `username` = '$username' LIMIT 1";
						if ($result = mysql_query($sql))
							return mysql_result($result, 0);
					}
				}

				return false;
			}

		function userInfo($id)
			{
				$id = (int) $id;

				if ($result = mysql_query("SELECT * FROM ". TFsBOX_DB_PREFIX ."users WHERE `id` = '$id' LIMIT 1"))
					return mysql_fetch_object($result);

				return false;
			}

		function wordFilters()
			{
				if (empty(self::$word_filters_cache))
					$this->refreshWordFilters();

				return self::$word_filters_cache;
			}
	}

?>
